﻿using System;
using System.Reflection;
using System.Runtime.InteropServices;

namespace PickGold.Api
{
	/// <summary/>
	public class WinSpool
	{
		/// <summary/>
		public const string DLL = ApiUtil.DLL_WINSPOOL;

		[DllImport(DLL, SetLastError = true, CharSet = CharSet.Auto)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool EnumPrinters([MarshalAs(UnmanagedType.U4)] PRINTER_ENUM flags, [MarshalAs(UnmanagedType.LPStr)] string sName, uint iLevel, IntPtr pPrinterDesc, uint iSize, [MarshalAs(UnmanagedType.U4)] ref uint iNeeded, [MarshalAs(UnmanagedType.U4)] ref uint iReturned);

		/// <summary>
		/// 检取一个标识特定打印机或打印服务器的句柄并打开
		/// </summary>
		/// <param name="szPrinter"></param>
		/// <param name="hPrinter"></param>
		/// <param name="pd"></param>
		/// <returns></returns>
		[DllImport(DLL, SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool OpenPrinterA([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

		/// <summary>
		/// 检取一个标识特定打印机或打印服务器的句柄并打开
		/// </summary>
		/// <param name="szPrinter"></param>
		/// <param name="hPrinter"></param>
		/// <param name="pd"></param>
		/// <returns></returns>
		[DllImport(DLL, SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool OpenPrinterA([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, PRINTER_DEFAULTS pd);

		/// <summary>
		/// 检取一个标识特定打印机或打印服务器的句柄并打开
		/// </summary>
		/// <param name="szPrinter"></param>
		/// <param name="hPrinter"></param>
		/// <param name="pd"></param>
		/// <returns></returns>
		[DllImport(DLL, SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

		/// <summary>
		/// 检取一个标识特定打印机或打印服务器的句柄并打开
		/// </summary>
		/// <param name="szPrinter"></param>
		/// <param name="hPrinter"></param>
		/// <param name="pd"></param>
		/// <returns></returns>
		[DllImport(DLL, SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, PRINTER_DEFAULTS pd);

		/// <summary/>
		[DllImport(DLL, SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool GetPrinterA(IntPtr hPrinter, Int32 dwLevel, IntPtr pPrinter, Int32 dwBuf, out Int32 dwNeeded);

		/// <summary/>
		[DllImport(DLL, SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool GetPrinter(IntPtr hPrinter, Int32 dwLevel, IntPtr pPrinter, Int32 dwBuf, out Int32 dwNeeded);

		/// <summary>
		/// 关闭给定的打印机对象
		/// </summary>
		/// <param name="hPrinter"></param>
		/// <returns></returns>
		[DllImport(DLL, SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool ClosePrinter(IntPtr hPrinter);

		/// <summary/>
		[DllImport(DLL, SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, [MarshalAs(UnmanagedType.LPStr)] string pDeviceNameg, IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode);

		/// <summary/>
		[DllImport(DLL, SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern int DocumentPropertiesA(IntPtr hwnd, IntPtr hPrinter, [MarshalAs(UnmanagedType.LPStr)] string pDeviceNameg, IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode);

		/// <summary>
		/// 通知假脱机打印程序将在假脱机上打印一个文档
		/// </summary>
		/// <param name="hPrinter"></param>
		/// <param name="level"></param>
		/// <param name="di"></param>
		/// <returns></returns>
		[DllImport(DLL, SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool StartDocPrinterA(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

		/// <summary>
		/// 通知假脱机打印程序将在假脱机上打印一个文档
		/// </summary>
		/// <param name="hPrinter"></param>
		/// <param name="level"></param>
		/// <param name="di"></param>
		/// <returns></returns>
		[DllImport(DLL, SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

		/// <summary/>
		[DllImport(DLL, CharSet = CharSet.Ansi, SetLastError = true)]
		public static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr pPrinter, int Command);

		/// <summary>
		/// 终止给定打印机的一个打印作业
		/// </summary>
		/// <param name="hPrinter"></param>
		/// <returns></returns>
		[DllImport(DLL, SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool EndDocPrinter(IntPtr hPrinter);

		/// <summary>
		/// 通知假脱机打印程序将在给定打印机上打印一页
		/// </summary>
		/// <param name="hPrinter"></param>
		/// <returns></returns>
		[DllImport(DLL, SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool StartPagePrinter(IntPtr hPrinter);

		/// <summary>
		/// 指示一页的结束和下一页的开始
		/// </summary>
		/// <param name="hPrinter"></param>
		/// <returns></returns>
		[DllImport(DLL, SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool EndPagePrinter(IntPtr hPrinter);

		/// <summary>
		/// 通知假脱机打印程序应向给定的打印机写指定的数据
		/// </summary>
		/// <param name="hPrinter"></param>
		/// <param name="pBytes"></param>
		/// <param name="dwCount"></param>
		/// <param name="dwWritten"></param>
		/// <returns></returns>
		[DllImport(DLL, SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, int dwCount, out int dwWritten);

		/// <summary>
		/// 通知假脱机打印程序应向给定的打印机写指定的数据
		/// </summary>
		/// <param name="hPrinter"></param>
		/// <param name="pBytes"></param>
		/// <param name="dwCount"></param>
		/// <param name="dwWritten"></param>
		/// <returns></returns>
		[DllImport(DLL, SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool WritePrinter(IntPtr hPrinter, byte[] pBytes, int dwCount, out int dwWritten);

		/// <summary>
		/// 从打印机读入数据
		/// 为使这个函数正常使用，端口必须是双向的
		/// </summary>
		/// <param name="hPrinter">一个已打开的打印机的句柄（用OpenPrinter获得）</param>
		/// <param name="pBuf">指定一个缓冲区或结构，用于装载来自打印机的数据</param>
		/// <param name="cdBuf">欲读入的缓冲区大小或字节数</param>
		/// <param name="pNoBytesRead">用于装载实际读取字节数的一个变量</param>
		/// <returns>非零表示成功，零表示失败。会设置GetLastError</returns>
		[DllImport(DLL, SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool ReadPrinter(IntPtr hPrinter, IntPtr pBuf, int cdBuf, ref int pNoBytesRead);

		/// <summary>
		/// 从打印机读入数据
		/// 为使这个函数正常使用，端口必须是双向的
		/// </summary>
		/// <param name="hPrinter">一个已打开的打印机的句柄（用OpenPrinter获得）</param>
		/// <param name="pBuf">指定一个缓冲区或结构，用于装载来自打印机的数据</param>
		/// <param name="cdBuf">欲读入的缓冲区大小或字节数</param>
		/// <param name="pNoBytesRead">用于装载实际读取字节数的一个变量</param>
		/// <returns>非零表示成功，零表示失败。会设置GetLastError</returns>
		[DllImport(DLL, SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool ReadPrinter(IntPtr hPrinter, byte[] pBuf, int cdBuf, ref int pNoBytesRead);

		/// <summary>
		/// When the function is given a printer name and an unmanaged array
		/// of bytes, the function sends those bytes to the print queue.
		/// Returns true on success, false on failure.
		/// </summary>
		/// <param name="szPrinterName">打印机名</param>
		/// <param name="szDataType">数据格式，空为 RAW</param>
		/// <param name="szTitle">文档标题，可空</param>
		/// <param name="pBytes">字节数组指针</param>
		/// <param name="dwCount">打印字节总数</param>
		/// <returns>写入结果</returns>
		public static bool PrintRaw(string szPrinterName, string szDataType, string szTitle, IntPtr pBytes, Int32 dwCount)
		{
			var dwWritten = 0;
			var hPrinter = IntPtr.Zero;
			var di = new DOCINFOA();
			var bSuccess = false; // Assume failure unless you specifically succeed.
			if (string.IsNullOrWhiteSpace(szTitle))
				szTitle = EnvironmentInitializer.HSJ + "'s Raw Document";
			di.pDocName = szTitle;
			if (string.IsNullOrWhiteSpace(szDataType))
				szDataType = "RAW";
			di.pDataType = szDataType;
			if (OpenPrinterA(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))// Open the printer.
			{
				if (StartDocPrinterA(hPrinter, 1, di))// Start a document.
				{
					if (StartPagePrinter(hPrinter))// Start a page.
					{
						bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);// Write bytes.
						EndPagePrinter(hPrinter);
					}
					EndDocPrinter(hPrinter);
				}
				ClosePrinter(hPrinter);
			}
			return bSuccess;
		}
		/// <summary>
		/// 输出打印机原始数据流到打印机
		/// </summary>
		/// <param name="szPrinterName">打印机名</param>
		/// <param name="szDataType">数据格式，空为 RAW</param>
		/// <param name="szTitle">文档标题，可空</param>
		/// <param name="pBytes">字节流</param>
		/// <returns>写入结果</returns>
		public static bool PrintRaw(string szPrinterName, string szDataType, string szTitle, byte[] pBytes)
		{
			var bSuccess = false;
			var nLength = pBytes.Length;
			var pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);// Unmanaged pointer. Allocate some unmanaged memory for those bytes.
			Marshal.Copy(pBytes, 0, pUnmanagedBytes, nLength);// Copy the managed byte array into the unmanaged array.
			bSuccess = PrintRaw(szPrinterName, szDataType, szTitle, pUnmanagedBytes, nLength);// Send the unmanaged bytes to the printer.
			Marshal.FreeCoTaskMem(pUnmanagedBytes);// Free the unmanaged memory that you allocated earlier.
			return bSuccess;
		}
		/// <summary>
		/// 输出打印机原始数据流到打印机，不推荐使用
		/// </summary>
		/// <param name="szPrinterName">打印机名</param>
		/// <param name="szDataType">数据格式，空为 RAW</param>
		/// <param name="szTitle">文档标题，可空</param>
		/// <param name="szString">打印内容</param>
		/// <returns>写入结果</returns>
		public static bool PrintRaw(string szPrinterName, string szDataType, string szTitle, string szString)
		{
			var dwCount = szString.Length;// How many characters are in the string?
			var pBytes = Marshal.StringToCoTaskMemAnsi(szString);// Assume that the printer is expecting ANSI text, and then convert the string to ANSI text.
			var bSuccess = PrintRaw(szPrinterName, szDataType, szTitle, pBytes, dwCount);// Send the converted ANSI string to the printer.
			Marshal.FreeCoTaskMem(pBytes);
			return bSuccess;
		}

		/// <summary>
		/// 遍历打印机
		/// </summary>
		/// <param name="flags"></param>
		/// <returns></returns>
		public static PRINTER_INFO_2[] EnumPrinters(PRINTER_ENUM flags)
		{
			var cbNeeded = 0U;
			var cReturned = 0U;
			var ret = EnumPrinters(flags, null, 2, IntPtr.Zero, 0, ref cbNeeded, ref cReturned);
			var pAddr = Marshal.AllocHGlobal((int)cbNeeded);
			ret = EnumPrinters(flags, null, 2, pAddr, cbNeeded, ref cbNeeded, ref cReturned);
			if (ret)
			{
				var offset = pAddr.ToInt32();
				var info2 = new PRINTER_INFO_2[cReturned];
				for (var i = 0; i < cReturned; i++)
				{
					info2[i] = (PRINTER_INFO_2)Marshal.PtrToStructure(new IntPtr(offset), typeof(PRINTER_INFO_2));
					offset += Marshal.SizeOf(typeof(PRINTER_INFO_2));
				}
				Marshal.FreeHGlobal(pAddr);
				return info2;
			}

			return null;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="szPrinterName"></param>
		/// <returns></returns>
		public static bool SupportsPostScript(string szPrinterName)
		{
			const int GETTECHNOLOGY = 20;
			const int QUERYESCSUPPORT = 8;
			const int POSTSCRIPT_PASSTHROUGH = 4115;
			const int ENCAPSULATED_POSTSCRIPT = 4116;

			const int POSTSCRIPT_IDENTIFY = 4117;
			const int POSTSCRIPT_INJECTION = 4118;
			const int POSTSCRIPT_DATA = 37;
			const int POSTSCRIPT_IGNORE = 38;

			var hDC = IntPtr.Zero; ;
			var hBLOB = IntPtr.Zero;
			var ps = new int[]
			{
				POSTSCRIPT_PASSTHROUGH,
				ENCAPSULATED_POSTSCRIPT,
				POSTSCRIPT_IDENTIFY,
				POSTSCRIPT_INJECTION,
				POSTSCRIPT_DATA,
				POSTSCRIPT_IGNORE,
			};
			try
			{
				var isz = 4;
				hDC = Gdi32.CreateDC(null, szPrinterName, IntPtr.Zero, IntPtr.Zero);

				hBLOB = Marshal.AllocCoTaskMem(isz);
				Marshal.WriteInt32(hBLOB, GETTECHNOLOGY);
				if (Gdi32.ExtEscape(hDC, QUERYESCSUPPORT, 4, hBLOB, 0, IntPtr.Zero) == 0)
					return false; // printer driver does not support GETTECHNOLOGY Checks.

				foreach (var val in ps)
				{
					Marshal.WriteInt32(hBLOB, val);
					if (Gdi32.ExtEscape(hDC, QUERYESCSUPPORT, isz, hBLOB, 0, IntPtr.Zero) != 0)
						return true; // if any of the checks pass, return true
				}
			}
			finally
			{
				if (hDC != IntPtr.Zero)
					Gdi32.DeleteDC(hDC);
				if (hBLOB != IntPtr.Zero)
					Marshal.Release(hBLOB);
			}
			return false;
		}

		/*
		AbortPrinter                  删除打印机的假脱机文件
		AddForm                       向可被选择用于给定打印机的格式表中添加一格式
		AddJob                        返回一个可用来存储打印工作的文件的完整路径和文件名
		AddMonitor                    安装一个本机打印机监视器，并连接配置文件、数据文件和监视器文件

		AddPort                       向支持的端口列表中添加一端口名
		AddPrinter                    向指定的服务器所支持的打印机列表中添加一打印机
		AddPrinterConnection          为当前用户添加指定的打印机并连接
		AddPrinterDriver              安装一本地或远程打印机并连接培植文件、数据文件和驱动文件
		AddPrintProcessor             在指定的服务器上安装一打印处理程序，并将它的名称添加到所支持的内部列表中
		AddPrintProvidor              安装一本地打印机提供程序，并连接配置文件、数据文件和提供程序文件
		AdvancedDocumentProperties    为给定的打印机显示一个打印机高级配置对话框，以允许进行配置
		ConfigurePort                 显示指定服务器上给定端口的配置对话框，以允许进行配置
		ConnectToPrinterDlg           显示一对话框供用户在网络浏览并连接打印机
		DeleteForm                    从所支持的格式表中删除一个格式名
		DeleteMonitor                 删除一个由AddMonitor函数所添加的打印机监视器
		DeleteProt                    显示一对话框，以允许用户删除一个端口名
		DeletePrinter                 删除指定的打印机对象
		DeletePrinterConnection       删除指定的打印机连接
		DeletePrinterDriver           从给定服务器所支持的驱动器名称表中删除指定的打印机驱动器
		DeletePrintProcessor          删除由AddPrintProcessor函数所添加的打印机处理程序
		DeletePrintProvidor           删除由AddPrintProvidor函数所添加的提供器
		DeviceCapabilities            获取指定的打印机所需求的性能

		DocumentProperties            为给定的打印机显示一个打印机配置对话框，以允许进行配置
		EnumForms                     枚举指定打印机所支持的格式
		EnumJobs                      用描述打印机的打印作业数据初始化一个JOB_INFO_1或JOB_INFO_2结构数组,以便枚举打印机作业
		EnumMonitors                  用描述给定服务器的监视器数据初始化一个 MONITOR_INFO_1结构数组,以便枚举打印机监视器
		EnumPorts                     枚举可用于在指定服务器上进行打印的端口
		EnumPrinterDrivers            枚举在给定打印机服务器上安装的所有打印机驱动程序
		EnumPrinters                  枚举指定服务器上可用的打印机
		EnumPrintProcessorDatatypes   枚举指定打印机处理程序所支持的数据类型
		EnumPrintProcessors           枚举在指定服务器上安装的打印处理器
		FindClosePrinterChangeNotification关闭通过调用FindFirstPrinterChangeNotification函数建立的改变通知对象
		FindFirstPrinterChangeNotification创建一个改变通知对象并返回句柄,使用该句柄在调用一个等待函数期间,检查打印机或打印服务器的变化
		FindNextPrinterChangeNotification为与指定打印机或打印服务器相关的改变通知对象检取最近的改变通知通知信息,也可用来重新设置该改变通知对象为不发信息状态
		FreePrinterNotifyInfo         释放系统分配的由FindNextPrinterChangeNotification函数返回的缓冲区

		GetForm                       利用描述给定打印机指定格式的数据初始化一个FORM_INFO_1结构
		GetJob                        检取指定打印机的打印作业数据
		GetPrinter                    检取给定打印机的数据
		GetPrinterData                检取给定打印机的配置数据
		GetPrinterDriver              检取给定打印机的驱动程序数据
		GetPrinterDriverDirectory     检取给定打印机驱动程序的目录路径
		GetPrintProcessDirectory      检取指定服务器上打印机处理程序的路径
		PrinterMessageBox             显示一个消息框,供一个正打印的应用程序通知用户发生一个打印作业错误
		PrinterProperties             为给定打印机显示一个打印机性能对话框,以允许进行设置
		ResetPrinter                  让应用程序设定有StartDocPrinter函数提交的打印文档的数据类型和设备模式值
		ScheduleJob                   通知假脱机打印程序,可为指定的作业安排打印
		SetForm                       为给定的打印机设置格式信息
		SetJob                        暂停,恢复,取消或重新启动给定打印机上指定的打印作业
		SetPrinter                    用暂停,恢复,或清除所有打印作业来设置指定的打印机
		SetPrinterData                设置一台打印机的配置数据
		*/
	}
}